/**
 * Fichier Enseignant.java : Définition d'une classe de base
 * fr.unice.gnpt.core.Enseignant : Gestion des Enseignants
 * 
 * Auteurs : Groupe GNPT : Gastinel, Nicolas, Pellegrino, Trovato
 */
package fr.unice.gnpt.core;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;

/**
 * Classe de gestion des enseignants.
 * <p>
 * Un enseignant est caractérisé par un couple (nom, prenom) unique. Il possède
 * un statut indiquant son niveau de compétences (définie par l'énumération
 * {@link StatutEnseignant}.
 * <p>
 * Son statut implique un nombre d'heures de TD à fournir disponible depuis
 * l'appel à la méthode {@link #getNombreHeuresDues()}. Son nombre d'heure de TD
 * effectué peut être connu à l'aide de la méthode
 * {@link #getNbTotalHeuresEquivalent()}.
 * <p>
 * Pour réaliser les heures nécessaires chaque enseignant contient une
 * collection de {@link Service}, indiquant les tâches qu'il doit réaliser.
 * 
 * @author gnpt
 * @version 0.0.3
 */
@Entity
@NamedQuery(name = "Enseignant.findByNomPrenom", query = "SELECT e FROM Enseignant e WHERE lower(e.nom) = :nom AND lower(e.prenom) = :prenom")
@Table(name = "Enseignant_gnpt")
public class Enseignant implements Comparable<Enseignant> {
	/**
	 * Représente un numéro de version (incrémenté à chaque modification),
	 * utilisé (par JPA) pour savoir si l’état d’une entité a été modifié entre
	 * 2 moments différents.
	 */
	private int version;
	/**
	 * Identifiant unique utilisé par JPA.
	 */
	private int id;
	private String nom;
	private String prenom;
	private StatutEnseignant statut;

	private int nbTotalHeuresEquivalent;
	private Collection<Service> services = new ArrayList<Service>();

	/**
	 * Constructeur par défaut de la classe Enseignant.
	 */
	public Enseignant() {
		this.services = new ArrayList<Service>();
	}

	/**
	 * Constructeur de la classe Enseignant.
	 * 
	 * @param nom
	 *            nom de l'enseignant.
	 * @param prenom
	 *            prénom de l'enseignant.
	 * @param statut
	 *            statut de l'enseignant.
	 * @see fr.unice.gnpt.core.StatutEnseignant
	 */
	public Enseignant(String nom, String prenom, StatutEnseignant statut) {
		this();
		this.nom = nom.toUpperCase();
		this.prenom = prenom;
		this.statut = statut;
	}

	/**
	 * Constructeur pour la classe Enseignant. Le statut de l'enseignant créé
	 * est Professeur {@code StatutEnseignant.PR}.
	 * 
	 * @param nom
	 *            le nom de l'enseignement.
	 * @param prenom
	 *            le prénom de l'enseignement.
	 */
	public Enseignant(String nom, String prenom) {
		this(nom, prenom, StatutEnseignant.PR);
	}

	/**
	 * Ajoute un service à l'enseignant courant et au cours associé, encapsulé
	 * dans l'enseignement passé en paramètre.
	 * 
	 * @param enseignement
	 *            l'enseignement à ajouter.
	 */
	public void ajouterEnseignement(Enseignement enseignement) {
		this.services.add(enseignement);
		enseignement.getCours().getEnseignements().add(enseignement);
	}

	/**
	 * Incrémente le nombre d'heure équivalent TD
	 * 
	 * @param enseignement
	 */
	public void ajouterAuNombreHeure(Enseignement enseignement) {
		double tmp = this.getNbTotalHeuresEquivalent();
		if (enseignement.estValide()) {
			if (enseignement.isCMEnCharge()) {
				tmp += enseignement.getCours().getNbHeuresCM() * 1.5;
			}
			tmp += (enseignement.getNbGroupesTDEnCharge()
					* enseignement.getCours().getNbHeuresTD() * 1);
			tmp += (enseignement.getNbGroupesTPEnCharge()
					* enseignement.getCours().getNbHeuresTP() * (1 / 1.5));
			this.setNbTotalHeuresEquivalent((int) Math.ceil(tmp));
		}
	}

	/**
	 * Ajoute une tâche administrative à l'enseignant courant.
	 * 
	 * @param ta
	 *            la tâche administrative à ajouter.
	 */
	public void ajouterTacheAdministrative(TacheAdministrative ta) {
		this.services.add(ta);
	}

	/**
	 * Enlève un service à l'enseignant courant et, si c'est un enseignement, au
	 * cours associé, encapsulé dans l'enseignement passé en paramètre.
	 * 
	 * @param service
	 *            le service à enelever.
	 */
	public void enleverService(Service service) {
		this.services.remove(service);
		if(service instanceof Enseignement)
		((Enseignement) service).getCours().getEnseignements().remove(service);
	}

	/**
	 * Accesseur pour l'identifiant unique caractérisant un enseignant.
	 * 
	 * @return l'identifiant unique caractérisant un enseignant.
	 */
	@Id
	@GeneratedValue
	public int getId() {
		return this.id;
	}

	/**
	 * Accesseur pour le nom de l'enseignant.
	 * 
	 * @return le nom de l'enseignant.
	 */
	@Column(name = "nom", nullable = false, length = 31)
	public String getNom() {
		return this.nom;
	}

	/**
	 * Accesseur pour le prénom de l'enseignant.
	 * 
	 * @return le prenom de l'enseignant.
	 */
	@Column(name = "prenom", nullable = false, length = 31)
	public String getPrenom() {
		return this.prenom;
	}

	/**
	 * Accesseur pour le statut de l'enseignant.
	 * 
	 * @return le statut.
	 */
	@Column(name = "statut", nullable = false)
	public StatutEnseignant getStatut() {
		return this.statut;
	}

	/**
	 * Accesseur pour le nombre d'heures (en hTD) dues par l'enseignant. Il est
	 * déterminé par le statut de l'enseignant.
	 * 
	 * @return le nombre d'hTD dues par l'enseignant.
	 */
	public int getNombreHeuresDues() {
		switch (getStatut()) {
		case PR:
		case MC:
			return 192;
		case PRAG:
			return 386;
		default:
			return 0;
		}
	}

	/**
	 * Calcule et retourne le nombre d'heure équivalent TD
	 * 
	 * @return le nombre total d'heures équivalent en heures de TD que
	 *         l'enseignant effectue.
	 */
	public int calcEtRetNbTotalHeuresEquivalent() {
		this.setNbTotalHeuresEquivalent(0);
		for (Service e : this.services) {
			if (e instanceof Enseignement)
				this.ajouterAuNombreHeure((Enseignement) e);
			else if (e instanceof TacheAdministrative)
				this.setNbTotalHeuresEquivalent(this
						.getNbTotalHeuresEquivalent()
						+ ((TacheAdministrative) e).getNbHeuresEquivalent());
		}
		return this.nbTotalHeuresEquivalent;
	}

	/**
	 * Accesseur simple pour le nombre total d'heures équivalent en heures de TD
	 * que l'enseignant effectue.
	 * 
	 * @return le nombre total d'heures équivalent en heures de TD que
	 *         l'enseignant effectue.
	 */
	public int getNbTotalHeuresEquivalent() {
		return this.nbTotalHeuresEquivalent;
	}

	/**
	 * Accesseur pour les services associés à l'enseignant.
	 * 
	 * @return les services associés à l'enseignant.
	 */
	@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "enseignant")
	public Collection<Service> getServices() {
		return this.services;
	}

	/**
	 * Accesseur pour les enseignements associés à l'enseignant.
	 * 
	 * @return les enseignements associés à l'enseignant.
	 */
	public Collection<Enseignement> getEnseignements() {
		Collection<Enseignement> coll = new ArrayList<Enseignement>();
		for (Service s : this.services)
			if (s instanceof Enseignement)
				coll.add((Enseignement) s);
		return coll;
	}

	/**
	 * Accesseur pour les tâches administratives associées à l'enseignant.
	 * 
	 * @return les tâches associés à l'enseignant.
	 */
	public Collection<TacheAdministrative> getTachesAdministratives() {
		Collection<TacheAdministrative> coll = new ArrayList<TacheAdministrative>();
		for (Service s : this.services)
			if (s instanceof TacheAdministrative)
				coll.add((TacheAdministrative) s);
		return coll;
	}

	/**
	 * Accesseur pour le numéro de version (incrémenté à chaque modification),
	 * utilisé (par JPA) pour savoir si l’état d’une entité a été modifié entre
	 * 2 moments différents.
	 * 
	 * @return le numéro de version.
	 */
	@Version
	public int getVersion() {
		return this.version;
	}

	/**
	 * Mutateur pour le numéro de version courant.
	 * 
	 * @param version
	 *            le nouveau numéro de version.
	 */
	public void setVersion(int version) {
		this.version = version;
	}

	/**
	 * Mutateur pour l'identifiant unique caractérisant un enseignant.
	 * 
	 * @param id
	 *            le nouveau identifiant unique caractérisant un enseignant.
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * Mutateur pour le nom de l'enseignant.
	 * 
	 * @param nom
	 *            le nouveau nom de l'enseignant.
	 */
	public void setNom(String nom) {
		this.nom = nom.toUpperCase();
	}

	/**
	 * Mutateur pour le prénom de l'enseignant.
	 * 
	 * @param prenom
	 *            le nouveau prénom de l'enseignant.
	 */
	public void setPrenom(String prenom) {
		this.prenom = prenom;
	}

	/**
	 * Mutateur pour le statut de l'enseignant.
	 * 
	 * @param statut
	 *            le nouveau statut de l'enseignant.
	 */
	public void setStatut(StatutEnseignant statut) {
		this.statut = statut;
	}

	/**
	 * Mutateur pour le nombre total d'heures équivalent en heures de TD que
	 * l'enseignant effectue.
	 * 
	 * @param nbTotalHeuresEquivalent
	 *            le nouveau nombre total d'heures équivalent en heures de TD
	 *            que l'enseignant effectue.
	 */
	public void setNbTotalHeuresEquivalent(int nbTotalHeuresEquivalent) {
		this.nbTotalHeuresEquivalent = nbTotalHeuresEquivalent;
	}

	/**
	 * Mutateur les services associés à l'enseignant.
	 * 
	 * @param services
	 *            la nouvelle collection de services associée à l'enseignant.
	 */
	public void setServices(Collection<Service> services) {
		this.services = services;
	}

	/**
	 * Accesseur pour récupérer le statut de l'enseignant sous forme de chaîne
	 * de caractère.
	 * 
	 * @return le statut de l'enseignant sous forme de chaîne de caractère.
	 */
	public String getStatutToString() {
		switch (this.statut) {
		case PR:
			return "Professeur";
		case MC:
			return "Maître de Conférence";
		case PRAG:
			return "Professeur Agrégé";
		default:
			return "Statut inconnu !";
		}
	}

	/**
	 * Description complète de l'enseignant.
	 * 
	 * @return renvoie une description complète de l'enseignant.
	 */
	public String fullToString() {
		StringBuffer outString = new StringBuffer();

		outString.append(this.getStatutToString());
		outString.append(" ");
		outString.append(this.nom + " " + this.prenom + "\n");
		outString.append(this.getNombreHeuresDues());
		outString.append(" h de TD équivalent dûes, ");
		outString.append(this.nbTotalHeuresEquivalent + " marquées");

		return outString.toString();
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return this.nom + " " + this.prenom;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Enseignant) {
			Enseignant e = (Enseignant) obj;
			// Peut-être comparer la liste de services ?
			return ((this.getNom().equalsIgnoreCase(e.getNom())) && (this
					.getPrenom().equalsIgnoreCase(e.getPrenom())));
		}
		return false;
	}

	/**
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(Enseignant o) {
		if (this.equals(o)) { return 0; }
		int comp = this.nom.compareTo(o.nom);
		if (comp == 0) { return this.prenom.compareTo(o.prenom); }
		return comp;
	}

	/**
	 * @return le nombre d'heure équivalent td non encore affecté
	 */
	public int getNbTotalHeuresEquivalentRestantes() {
		return this.getNombreHeuresDues() - this.getNbTotalHeuresEquivalent();
	}

}
